package com.yourenbang.lingyun.transaction.web.service.impl.pay.withdraw.channel;

import cn.hutool.core.text.UnicodeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.enums.PlatNoEnums;
import com.yourenbang.lingyun.constants.enums.pay.PayFromEnum;
import com.yourenbang.lingyun.constants.enums.pay.PayTypeEnum;
import com.yourenbang.lingyun.core.exception.UserBizException;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.util.BizAssertUtils;
import com.yourenbang.lingyun.core.util.DingdingUtils;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.core.util.StringUtil;
import com.yourenbang.lingyun.core.util.date.LocalDateUtils;
import com.yourenbang.lingyun.core.util.http.HttpClientUtil;
import com.yourenbang.lingyun.core.util.json.JsonUtils;
import com.yourenbang.lingyun.domain.MerchantSignHjtx;
import com.yourenbang.lingyun.domain.MerchantSignHjtxExample;
import com.yourenbang.lingyun.domain.UserBase;
import com.yourenbang.lingyun.mapper.MerchantSignHjtxMapper;
import com.yourenbang.lingyun.mapper.UserBaseMapper;
import com.yourenbang.lingyun.transaction.config.DingdingConfig;
import com.yourenbang.lingyun.transaction.exception.WichdrawFailException;
import com.yourenbang.lingyun.transaction.exception.WichdrawWaitException;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountWithdrawPaymentMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionPayTypeMapper;
import com.yourenbang.lingyun.transaction.util.hjtx.HjtxRSAUtils;
import com.yourenbang.lingyun.transaction.util.hjtx.Sha256;
import com.yourenbang.lingyun.transaction.web.domain.dto.pay.hjtx.*;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountWithdrawPayment;
import com.yourenbang.lingyun.transaction.web.domain.vo.pay.hjtx.AgentpayVO;
import com.yourenbang.lingyun.transaction.web.service.pay.withdraw.channel.HjtxService;
import com.yourenbang.transaction.api.service.pay.PayTypeApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service("hjtxService")
public class HjtxServiceImpl implements HjtxService {

    @Autowired
    private DingdingConfig dingdingConfig;
    @Autowired
    private UserBaseMapper userBaseMapper;
    @Autowired
    private MerchantSignHjtxMapper merchantSignHjtxMapper;
    @Autowired
    private TransactionPayTypeMapper transactionPayTypeMapper;
    @Autowired
    private TransactionFundAccountWithdrawPaymentMapper fundAccountWithdrawPaymentMapper;
    @Autowired
    private PayTypeApiService payTypeApiService;

    /**
     * 代付
     */
    @Override
    public AgentpayVO agentpay(HjtxAgentpayDTO dto) {
        KLog.info("【汇积天下代付】入参:{}", dto.toJSON());
        Map<String, String> typeParams = payTypeApiService.getPayParamsByChannel(dto.getPayChannelId());
        HjtxAgentpayParamDTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamDTO.class);

        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("body", StringUtils.isBlank(dto.getRemark()) ? "收益" : dto.getRemark());
        contentMap.put("id_card", dto.getIdCard());
        contentMap.put("subject", "考拉快收");
        contentMap.put("out_trade_no", dto.getOrderNo());
        contentMap.put("phone", dto.getPhone());
        contentMap.put("accept_name", dto.getAccountName());
        contentMap.put("accept_card", dto.getAccountNo());
        contentMap.put("amount", dto.getAmt().setScale(2, RoundingMode.HALF_UP).toString());
        contentMap.put("bank_remark", StringUtils.isBlank(dto.getRemark()) ? "收益" : dto.getRemark());
        if (StringUtils.isNotBlank(paramDTO.getBusinessType())) {
            contentMap.put("business_type", paramDTO.getBusinessType());
            contentMap.put("performance", dto.getAmt().setScale(2, RoundingMode.HALF_UP)
                    .divide(new BigDecimal(paramDTO.getAccountMark()), 2, RoundingMode.HALF_UP).toString());
            contentMap.put("account_mark", paramDTO.getAccountMark());
            contentMap.put("task_mark", paramDTO.getTaskMark());
        }

        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setAppid(paramDTO.getAppid());
        baseDTO.setMethod("agentpay");
        baseDTO.setNotify_url(paramDTO.getAgentpaycallurl());

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下代付】加密前参数:{},入参:{}", contentMap, dto.toJSON());
            baseDTO.setBiz_content(
                    HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap, true), kuaishouPrivateKey)
            );
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下代付】准备请求接口,url:{}, 请求参数:{},入参:{}", url, baseDTO.toJSON(), dto.toJSON());
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下代付】接口响应:{},入参:{}", resp, dto.toJSON());

        } catch (Exception e) {
            KLog.warn("【汇积天下代付】请求异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下代付】接口返回空,入参:{}", dto.toJSON());
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, "接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        if (!respObj.getBoolean("res")) {
            KLog.warn("【汇积天下代付】接口响应未成功,入参:{}", dto.toJSON());
            if (StringUtils.equals(respObj.getString("code"), AgentpayVO.STATUS_207017)) {      //{"res":false,"code":"207017","msg":"该商家备付金不足"}
                String msg = "【汇积天下代付-预警】" + resp + LocalDateTime.now();
                DingdingUtils.sendToDingding(msg, dingdingConfig.getDingdingWarnToken());
                //关闭支付方式
                transactionPayTypeMapper.closePayTypeByChannelId(dto.getPayChannelId());
                throw new WichdrawFailException("通道维护中,请稍后再试");
            }else if(StringUtils.equals(respObj.getString("code"), AgentpayVO.STATUS_219025)){     //{"res":false,"code":"219025","msg":" 用户月金额达到上限: 98000.00"}
                throw new WichdrawFailException(respObj.getString("msg"));
            }else if(StringUtils.equals(respObj.getString("code"), AgentpayVO.STATUS_214043)){     //{"res":false,"code":"214043","msg":"用户未签约"}
                throw new WichdrawFailException(respObj.getString("msg"));
            }else if(StringUtils.equals(respObj.getString("code"), AgentpayVO.STATUS_214037)){     //{"res":false,"code":"214037","msg":"年龄大于65周岁"}
                throw new WichdrawFailException(respObj.getString("msg"));
            }
            throw new WichdrawWaitException(respObj.getString("msg"));
        }
        String resDataStr = respObj.getString("data");
        JSONObject respData = JSON.parseObject(resDataStr);

        try {
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            if (!HjtxRSAUtils.rsa256Check(
                    HjtxRSAUtils.getEncodeSignContentByMap(formatMap(respData), false), respData.getString("sign"), hjtxPublicKey
            )) {
                KLog.error("【汇积天下代付】验签失败,入参:{}", dto.toJSON());
                throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, "验签失败");
            }
        } catch (Exception e) {
            KLog.error("【汇积天下代付】验签异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, e.getMessage());
        }

        if (!StringUtils.equalsAny(respData.getString("trade_status"), "SUCCESS", "PROCESS")) {
            KLog.warn("【汇积天下代付】接口响应未成功,入参:{}", dto.toJSON());
            throw new WichdrawFailException(respData.getString("message"));
        }

        AgentpayVO vo = new AgentpayVO();
        vo.setCode(respObj.getString("code"));
        vo.setMessage(respObj.getString("msg"));
        vo.setOrderNo(dto.getOrderNo());
        vo.setSerialNumber(respData.getString("trade_no"));
        return vo;
    }

    /**
     * 代付查询
     */
    @Override
    public void agentpayQuery(HjtxAgentpayQueryDTO dto) {
        Map<String, String> typeParams = payTypeApiService.getPayParamsByChannel(dto.getPayChannelId());
        HjtxAgentpayParamDTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamDTO.class);
        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("out_trade_no", dto.getOrderNo());
        contentMap.put("trade_no", dto.getOutOrderNo());


        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setAppid(paramDTO.getAppid());
        baseDTO.setMethod("tradeInfo");

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下代付查询】加密前参数:{},入参:{}", contentMap, dto.toJSON());
            baseDTO.setBiz_content(HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap, true), kuaishouPrivateKey));
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下代付查询】准备请求接口,url:{}, 请求参数:{},入参:{}", url, baseDTO.toJSON(), dto.toJSON());
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下代付查询】接口响应:{},入参:{}", resp, dto.toJSON());

        } catch (Exception e) {
            KLog.warn("【汇积天下代付查询】请求失败:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下代付查询】接口返回空,入参:{}", dto.toJSON());
            throw new WichdrawWaitException("接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        if (!respObj.getBoolean("res")) {
            KLog.warn("【汇积天下代付查询】接口响应未成功,入参:{}", dto.toJSON());
            throw new WichdrawWaitException(respObj.getString("msg"));
        }
        String resDataStr = respObj.getString("data");
        JSONObject respData = JSON.parseObject(resDataStr);

        try {
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            if (!HjtxRSAUtils.rsa256Check(HjtxRSAUtils.getEncodeSignContentByMap(formatMap(respData), false), respData.getString("sign"), hjtxPublicKey)) {
                KLog.error("【汇积天下代付查询】验签失败,入参:{}", dto.toJSON());
                throw new WichdrawWaitException("验签失败");
            }
        } catch (Exception e) {
            KLog.error("【汇积天下代付查询】验签异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new WichdrawWaitException(e.getMessage());
        }

        //明确失败
        if (StringUtils.equals(respData.getString("trade_status"), "FAIL")) {
            KLog.warn("【汇积天下代付查询】接口响应失败,入参:{}", dto.toJSON());
            throw new WichdrawFailException("接口响应失败,接口返回:" + resp);
        }

        //非成功
        if (!StringUtils.equals(respData.getString("trade_status"), "SUCCESS")) {
            KLog.warn("【汇积天下代付查询】接口响应未成功,入参:{}", dto.toJSON());
            throw new WichdrawWaitException("接口响应未成功,接口返回:" + resp);
        }
    }

    /**
     * 回调
     */
    @Override
    public FundAccountWithdrawPayment notice(Map<String, String> param) {
        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTX, PayFromEnum.withdraw_ally);
        HjtxAgentpayParamDTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamDTO.class);
        JSONObject dataObj = JSON.parseObject(param.get("data"));
        //校验签名
        try {
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            if (!HjtxRSAUtils.rsa256Check(
                    HjtxRSAUtils.getEncodeSignContentByMap(formatMap(dataObj), false), dataObj.getString("sign"), hjtxPublicKey
            )) {
                KLog.error("【汇积天下提现回调】验签失败, 入参:{}", param);
                throw new WichdrawWaitException("验签失败,接口返回:" + param);
            }
        } catch (Exception e) {
            KLog.error("【汇积天下提现回调】验签异常:{},入参:{}", e.getMessage(), param);
            throw new WichdrawWaitException(e.getMessage());
        }

        String orderSn = dataObj.getString("out_trade_no");

        Short platNo = new Short(StringUtils.right(orderSn, 2));
        FundAccountWithdrawPayment payment = fundAccountWithdrawPaymentMapper.findByOrderSn(platNo, orderSn);
        if (payment == null) {
            KLog.warn("【汇积天下提现回调】未找到提现订单,入参:{}", param);
            throw new WichdrawWaitException("未找到提现订单");
        }
        payment.setPlatNo(platNo);

        //明确失败
        if (StringUtils.equals(dataObj.getString("trade_status"), "FAIL")) {
            KLog.warn("【汇积天下提现回调】接口响应失败,入参:{}", param);
            throw new WichdrawFailException("接口响应失败");
        }

        //非成功
        if (!StringUtils.equals(dataObj.getString("trade_status"), "SUCCESS")) {
            KLog.warn("【汇积天下提现回调】接口响应未成功,入参:{}", param);
            throw new WichdrawWaitException("接口响应未成功");
        }
        return payment;
    }

    /**
     * 汇积天下签约回调
     */
    @Override
    public void withdrawSigneHjtx(String param) {
        KLog.info("【汇积天下签约回调】入参:{}", param);

        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTX, PayFromEnum.withdraw_ally);
        HjtxAgentpayParamDTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamDTO.class);
        JSONObject respDataOjb = JSON.parseObject(param);
        JSONObject bizContentOjj = JSON.parseObject(respDataOjb.getString("bizContent"));
        bizContentOjj.put("stampNum", respDataOjb.get("stampNum"));

        if (!StringUtils.equals(bizContentOjj.getString("lklSignatureFlag"), "1")) {
            KLog.warn("【汇积天下签约回调】签约失败,入参:{}", param);
            return;
        }

        String checkSign = Sha256.signBySha256(bizContentOjj, paramDTO.getSha256key());
        //校验签名
        if (!StringUtils.equals(checkSign, respDataOjb.getString("sign"))) {
            KLog.error("【汇积天下签约回调】验签失败, 计算的签名:{}, 报文的签名:{}, 入参:{}", checkSign, respDataOjb.getString("sign"), param);
            throw new WichdrawWaitException("验签失败,接口返回:" + param);
        }

        UserBase userBase = userBaseMapper.selectByPrimaryKey(bizContentOjj.getString("agentNum"));
        if (userBase == null) {
            KLog.warn("【汇积天下签约回调】未找到商户信息,入参:{}", param);
            return;
        }

        if (alreadySign(userBase.getUserBaseId())) {
            KLog.warn("【汇积天下签约回调】用户已签约,入参:{}", param);
            return;
        }

        MerchantSignHjtx signHjtx = new MerchantSignHjtx();
        signHjtx.setUserBaseId(userBase.getUserBaseId());
        signHjtx.setIdNumber(userBase.getIdNumber());
        signHjtx.setCallbackData(param);
        int inSing = merchantSignHjtxMapper.insertSelective(signHjtx);
        BizAssertUtils.assertOne(inSing, "添加签约记录失败");

    }

    /**
     * 格式化 object map
     */
    protected Map<String, String> formatMap(Map<String, Object> objectMap) {
        Map<String, String> strMap = new HashMap<>();
        for (String key : objectMap.keySet()) {
            strMap.put(key, objectMap.get(key) + "");
        }
        return strMap;
    }

    /**
     * 是否签约
     * true : 已签约， false : 未签约
     */
    @Override
    public Boolean alreadySign(String userBaseId) {

        UserBase userBase = userBaseMapper.selectByPrimaryKey(userBaseId);
        if (userBase == null) {
            return false;
        }
        if (StringUtils.isBlank(userBase.getIdNumber())) {
            return false;
        }
        MerchantSignHjtxExample hjtxExample = new MerchantSignHjtxExample();
        hjtxExample.createCriteria()
                .andIdNumberEqualTo(userBase.getIdNumber())
                .andUserBaseIdEqualTo(userBaseId);
        List<MerchantSignHjtx> signHjtxs = merchantSignHjtxMapper.selectByExample(hjtxExample);

        if (CollectionUtils.isEmpty(signHjtxs)) {

            if (PlatNoEnums.YXY.isEquals(userBase.getPlatNo())) {
                // 调用接口查询签约状态
                Result checkResult = checkUserSignInfo(PayFromEnum.yxy_withdraw_ally_all, userBaseId);
                if (Objects.nonNull(checkResult) && checkResult.isSuccess()) {
                    String signUrl = (String) checkResult.getData();
                    if (StringUtils.isBlank(signUrl)) {
                        // 签约地址为空，说明已签约
                        saveMerchantSignHjtx(userBaseId, userBase.getIdNumber(), null);
                        return Boolean.TRUE;
                    }
                }
            }
            return Boolean.FALSE;
        } else {
            return Boolean.TRUE;
        }
    }

    /**
     * 获取汇积天下签约地址
     */
    @Override
    public String fullSingUrl(PayFromEnum payFrom, String userBaseId) {
        UserBase userBase = userBaseMapper.selectByPrimaryKey(userBaseId);
        if (userBase == null) {
            return StringUtils.EMPTY;
        }

        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTX, payFrom);
        HjtxAgentpayParamDTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamDTO.class);
        //加密身份证号
        String encryptIdCard = DigestUtils.sha256Hex(userBase.getIdNumber() + paramDTO.getSha256key());
        //完整的认证地址
        String fullSignUrl = paramDTO.getSignurl() + "&agentNum=${agentNum}&encryptIdCard=${encryptIdCard}";

        Map<String, String> parseMap = new HashMap<>();
        parseMap.put("agentNum", userBaseId);
        parseMap.put("encryptIdCard", encryptIdCard);
        String finalUrl = StringUtil.parseVariable(fullSignUrl, parseMap);
        KLog.info("[获取汇积天下签约地址]出参:{}, userBaseId:{}", finalUrl, userBaseId);
        return finalUrl;
    }

/***************** 汇积天下V2版本接口 ******************/

    /**
     * 代付V2
     */
    @Override
    public AgentpayVO agentpayV2(HjtxAgentpayDTO dto) {

        Result checkRes = checkQualified(dto);
        if (checkRes.isFail()) {
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, "检查是否符合发佣要求失败");
        }

        KLog.info("【汇积天下代付V2】入参:{}", dto.toJSON());
        Map<String, String> typeParams = payTypeApiService.getPayParamsByChannel(dto.getPayChannelId());
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);

        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("id_card", dto.getIdCard());
        contentMap.put("phone", dto.getPhone());
        contentMap.put("out_trade_no", dto.getOrderNo());
        contentMap.put("accept_name", dto.getAccountName());
        contentMap.put("accept_card", dto.getAccountNo());
        contentMap.put("amount", dto.getAmt().setScale(2, RoundingMode.HALF_UP).toString());
        contentMap.put("business_type", paramDTO.getBusinessType());
        // 完成业绩描述，业绩=服务费/结算标准 填金额：
        String achievement = dto.getAmt().divide(new BigDecimal(paramDTO.getCalculationStandard()))
                .setScale(2, RoundingMode.HALF_UP).toString();
        contentMap.put("achievement", achievement);
        contentMap.put("calculation_standard",paramDTO.getCalculationStandard());
        contentMap.put("task_remark", paramDTO.getTaskRemark());

        String taskCode = getHjtxTaskCode(paramDTO.getTaskCode());
        if (StringUtils.isBlank(taskCode)) {
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, "解析taskCode失败");
        } else {
            contentMap.put("task_code", taskCode);
        }

        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setChannel(paramDTO.getChannel());
        baseDTO.setMethod("agentPayment");

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下代付V2】加密前参数:{},入参:{}", contentMap, dto.toJSON());
            baseDTO.setBiz_content(
                    HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap, true), kuaishouPrivateKey)
            );
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下代付V2】准备请求接口,url:{}, 请求参数:{},入参:{}", url, baseDTO.toJSON(), dto.toJSON());
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下代付V2】接口响应:{},入参:{}", resp, dto.toJSON());

        } catch (Exception e) {
            KLog.warn("【汇积天下代付V2】请求异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下代付V2】接口返回空,入参:{}", dto.toJSON());
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, "接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        JSONObject respData;
        String resDataStr;
        try {
            String resDataEncrypt = respObj.getString("data");
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            // 公钥解密
            resDataStr = HjtxRSAUtils.decryptByPublicKey(resDataEncrypt, hjtxPublicKey);
            resDataStr = UnicodeUtil.toString(resDataStr);
            respData = JSON.parseObject(resDataStr);
            KLog.info("【汇积天下代付V2】响应结果解密后：{}", resDataStr);

        } catch (Exception e) {
            KLog.error("【汇积天下代付V2】验签异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new UserBizException(BizCodeMessages.UNKNOWN_ERROR, e.getMessage());
        }

        if (StringUtils.contains(resDataStr, "该商家备付金不足")) {
            KLog.warn("【汇积天下代付V2】该商家备付金不足,入参:{}", dto.toJSON());
            String msg = "【汇积天下代付V2-预警】" + resDataStr + LocalDateTime.now();
            DingdingUtils.sendToDingding(msg, dingdingConfig.getDingdingWarnToken());
            //关闭支付方式
            transactionPayTypeMapper.closePayTypeByChannelId(dto.getPayChannelId());
            throw new WichdrawFailException("通道维护中,请稍后再试");
        }

        if (!StringUtils.equalsAny(respData.getString("trade_status"), "SUCCESS", "PROCESS")) {
            KLog.warn("【汇积天下代付V2】接口响应未成功,入参:{}", dto.toJSON());
            throw new WichdrawFailException(respData.getString("fail_msg"));
        }

        AgentpayVO vo = new AgentpayVO();
        vo.setCode(respObj.getString("code"));
        vo.setMessage(respObj.getString("msg"));
        vo.setOrderNo(dto.getOrderNo());
        vo.setSerialNumber(respData.getString("trade_no"));
        return vo;
    }

    /**
     * 代付查询V2
     */
    @Override
    public void agentpayQueryV2(HjtxAgentpayQueryDTO dto) {
        Map<String, String> typeParams = payTypeApiService.getPayParamsByChannel(dto.getPayChannelId());
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);
        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("out_trade_no", dto.getOrderNo());
        contentMap.put("trade_no", dto.getOutOrderNo());

        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setChannel(paramDTO.getChannel());
        baseDTO.setMethod("getTradeInfo");

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下代付查询V2】加密前参数:{},入参:{}", contentMap, dto.toJSON());
            baseDTO.setBiz_content(HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap, true), kuaishouPrivateKey));
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下代付查询V2】准备请求接口,url:{}, 请求参数:{},入参:{}", url, baseDTO.toJSON(), dto.toJSON());
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下代付查询V2】接口响应:{},入参:{}", resp, dto.toJSON());

        } catch (Exception e) {
            KLog.warn("【汇积天下代付查询V2】请求失败:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下代付查询V2】接口返回空,入参:{}", dto.toJSON());
            throw new WichdrawWaitException("接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        if (!respObj.getBoolean("res")) {
            KLog.warn("【汇积天下代付查询V2】接口响应未成功,入参:{}", dto.toJSON());
            throw new WichdrawWaitException(respObj.getString("msg"));
        }
        JSONObject respData;

        try {
            String resDataEncrypt = respObj.getString("data");
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            // 公钥解密
            String resDataStr = HjtxRSAUtils.decryptByPublicKey(resDataEncrypt, hjtxPublicKey);
            respData = JSON.parseObject(resDataStr);

            KLog.info("【汇积天下代付查询V2】响应结果解密后：{}", UnicodeUtil.toString(resDataStr));
        } catch (Exception e) {
            KLog.error("【汇积天下代付查询V2】验签异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new WichdrawWaitException(e.getMessage());
        }

        //明确失败
        if (StringUtils.equals(respData.getString("trade_status"), "FAIL")) {
            KLog.warn("【汇积天下代付查询V2】接口响应失败,入参:{}", dto.toJSON());
            String failMsg = respData.getString("fail_msg");
            throw new WichdrawFailException("接口响应失败,接口返回:" + failMsg);
        }

        //非成功
        if (!StringUtils.equals(respData.getString("trade_status"), "SUCCESS")) {
            KLog.warn("【汇积天下代付查询V2】订单状态为非成功,入参:{}", dto.toJSON());
            throw new WichdrawWaitException("订单状态非成功,接口返回:" + resp);
        }
    }


    /**
     * 提现回调V2
     */
    @Override
    public FundAccountWithdrawPayment noticeV2(Map<String, String> param) {
        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTX, PayFromEnum.withdraw_ally);
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);
        JSONObject dataObj = JSON.parseObject(param.get("data"));
        //校验签名 todo
//        try {
//            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
//            if (!HjtxRSAUtils.rsa256Check(
//                    HjtxRSAUtils.getEncodeSignContentByMap(formatMap(dataObj), false), dataObj.getString("sign"), hjtxPublicKey
//            )) {
//                KLog.error("【汇积天下提现回调V2】验签失败, 入参:{}", param);
//                throw new WichdrawWaitException("验签失败,接口返回:" + param);
//            }
//        } catch (Exception e) {
//            KLog.error("【汇积天下提现回调V2】验签异常:{},入参:{}", e.getMessage(), param);
//            throw new WichdrawWaitException(e.getMessage());
//        }

        String orderSn = dataObj.getString("out_trade_no");

        Short platNo = new Short(StringUtils.right(orderSn, 2));
        FundAccountWithdrawPayment payment = fundAccountWithdrawPaymentMapper.findByOrderSn(platNo, orderSn);
        if (payment == null) {
            KLog.warn("【汇积天下提现回调V2】未找到提现订单,入参:{}", param);
            throw new WichdrawWaitException("未找到提现订单");
        }
        payment.setPlatNo(platNo);

        //明确失败
        if (StringUtils.equals(dataObj.getString("trade_status"), "FAIL")) {
            KLog.warn("【汇积天下提现回调V2】接口响应失败,入参:{}", param);
            throw new WichdrawFailException("接口响应失败");
        }

        //非成功
        if (!StringUtils.equals(dataObj.getString("trade_status"), "SUCCESS")) {
            KLog.warn("【汇积天下提现回调V2】接口响应未成功,入参:{}", param);
            throw new WichdrawWaitException("接口响应未成功");
        }
        return payment;
    }


    /**
     * 查询是否符合发佣要求
     */
    @Override
    public Result checkQualified(HjtxAgentpayDTO dto) {
        log.info("【汇积天下查询是否符合发佣要求】入参:{}", dto.toJSON());
        Map<String, String> typeParams = payTypeApiService.getPayParamsByChannel(dto.getPayChannelId());
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);
        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("id_card", dto.getIdCard());
        contentMap.put("amount", dto.getAmt().toString());

        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setChannel(paramDTO.getChannel());
        baseDTO.setMethod("checkQualified");

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下查询是否符合发佣要求】加密前参数:{},入参:{}", contentMap, dto.toJSON());
            baseDTO.setBiz_content(HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap, true), kuaishouPrivateKey));
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下查询是否符合发佣要求】准备请求接口,url:{}, 请求参数:{},入参:{}", url, baseDTO.toJSON(), dto.toJSON());
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下查询是否符合发佣要求】接口响应:{},入参:{}", resp, dto.toJSON());

        } catch (Exception e) {
            KLog.warn("【汇积天下查询是否符合发佣要求】请求失败:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下查询是否符合发佣要求】接口返回空,入参:{}", dto.toJSON());
            throw new WichdrawWaitException("接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        if (!respObj.getBoolean("res")) {
            KLog.warn("【汇积天下查询是否符合发佣要求】接口响应未成功,入参:{}", dto.toJSON());
            throw new WichdrawWaitException(respObj.getString("msg"));
        }
        String resDataEncrypt = respObj.getString("data");
        JSONObject respData;

        try {
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            // 公钥解密
            String resDataStr = HjtxRSAUtils.decryptByPublicKey(resDataEncrypt, hjtxPublicKey);
            respData = JSON.parseObject(resDataStr);
            KLog.info("【汇积天下查询是否符合发佣要求】响应结果解密后：{}", UnicodeUtil.toString(resDataStr));
        } catch (Exception e) {
            KLog.error("【汇积天下查询是否符合发佣要求】验签异常:{},入参:{}", e.getMessage(), dto.toJSON());
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.equals(respData.getString("res_code"), "000000")) {
            return Result.builder().success(Boolean.TRUE);
        } else {
            return Result.builder().fail();
        }
    }


    /**
     * 获取汇积天下签约地址V2
     */
    @Override
    public String fullSingUrlV2(PayFromEnum payFrom, String userBaseId) {
        UserBase userBase = userBaseMapper.selectByPrimaryKey(userBaseId);
        if (userBase == null) {
            return StringUtils.EMPTY;
        }

        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTXV2, payFrom);
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);
        //加密身份证号
        String encryptIdCard = DigestUtils.sha256Hex(userBase.getIdNumber() + paramDTO.getSha256key());
        //完整的认证地址
        String fullSignUrl = paramDTO.getSignurl() + "&agentNum=${agentNum}&encryptIdCard=${encryptIdCard}&channel=${channel}";

        Map<String, String> parseMap = new HashMap<>();
        parseMap.put("agentNum", userBaseId);
        parseMap.put("encryptIdCard", encryptIdCard);
        parseMap.put("channel", paramDTO.getChannel());
        String finalUrl = StringUtil.parseVariable(fullSignUrl, parseMap);
        KLog.info("[获取汇积天下签约地址]出参:{}, userBaseId:{}", finalUrl, userBaseId);
        return finalUrl;
    }


    /**
     * 查询用户签约
     */
    @Override
    public Result checkUserSignInfo(PayFromEnum payFrom, String userBaseId) {
        log.info("汇积天下查询用户签约, userBaseId={}", userBaseId);
        UserBase userBase = userBaseMapper.selectByPrimaryKey(userBaseId);
        if (userBase == null) {
            return Result.builder().fail(BizCodeMessages.PARAMETER_ERR);
        }

        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTXV2, payFrom);
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);

        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("id_card", userBase.getIdNumber());
        contentMap.put("accept_name", userBase.getUserName());
        contentMap.put("agent_user_num", userBaseId);

        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setChannel(paramDTO.getChannel());
        baseDTO.setMethod("getUserSignInfo");

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下查询用户签约】加密前参数:{}", contentMap);
            baseDTO.setBiz_content(HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap,
                    true), kuaishouPrivateKey));
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下查询用户签约】准备请求接口,url:{}, 请求参数:{},入参:{}", url, baseDTO.toJSON(), userBaseId);
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下查询用户签约】接口响应:{},入参:{}", resp, userBaseId);

        } catch (Exception e) {
            KLog.warn("【汇积天下查询用户签约】请求失败:{},入参:{}", e.getMessage(), userBaseId);
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下查询用户签约】接口返回空,入参:{}", userBaseId);
            throw new WichdrawWaitException("接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        if (!respObj.getBoolean("res")) {
            KLog.warn("【汇积天下查询用户签约】接口响应未成功,入参:{}", userBaseId);
            throw new WichdrawWaitException(respObj.getString("msg"));
        }
        JSONObject respData;

        try {
            String resDataEncrypt = respObj.getString("data");
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            // 公钥解密
            String resDataStr = HjtxRSAUtils.decryptByPublicKey(resDataEncrypt, hjtxPublicKey);
            respData = JSON.parseObject(resDataStr);
            KLog.info("【汇积天下查询用户签约】响应结果解密后：{}", UnicodeUtil.toString(resDataStr));

        } catch (Exception e) {
            KLog.error("【汇积天下查询用户签约】响应结果解密异常:{},入参:{}", e.getMessage(), userBaseId);
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.equals(respData.getString("res_code"), "000000")) {
            // 已签约
            return Result.builder().success();
        } else if (StringUtils.equals(respData.getString("res_code"), "400002")) {

            JSONObject bizContent = respData.getJSONObject("biz_content");
            String signUrl =  bizContent.getString("sign_url");
            return Result.builder().success(signUrl);
        } else {
            throw new WichdrawWaitException("【汇积天下查询用户签约】响应结果状态异常");
        }
    }

    /**
     * 汇积天下签约回调 (V2)
     *
     * @param param
     */
    @Override
    public void withdrawSigneHjtxV2(String param) {
        KLog.info("【汇积天下签约回调V2】入参:{}", param);

        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTXV2, PayFromEnum.yxy_withdraw_ally_all);
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);
        JSONObject respDataOjb = JSON.parseObject(param);
        JSONObject bizContentObj = JSON.parseObject(respDataOjb.getString("bizContent"));
        bizContentObj.put("stampNum", respDataOjb.get("stampNum"));

        if (!StringUtils.equals(bizContentObj.getString("lklSignatureFlag"), "1")) {
            KLog.warn("【汇积天下签约回调V2】签约失败,入参:{}", param);
            return;
        }

        // TODO
//        String checkSign = Sha256.signBySha256(bizContentObj, paramDTO.getSha256key());
//        //校验签名
//        if (!StringUtils.equals(checkSign, respDataOjb.getString("sign"))) {
//            KLog.error("【汇积天下签约回调V2】验签失败, 计算的签名:{}, 报文的签名:{}, 入参:{}", checkSign, respDataOjb.getString("sign"), param);
//            throw new WichdrawWaitException("验签失败,接口返回:" + param);
//        }

        UserBase userBase = userBaseMapper.selectByPrimaryKey(bizContentObj.getString("agentNum"));
        if (userBase == null) {
            KLog.warn("【汇积天下签约回调V2】未找到商户信息,入参:{}", param);
            return;
        }

        if (alreadySign(userBase.getUserBaseId())) {
            KLog.info("【汇积天下签约回调V2】用户已签约,入参:{}", param);
        }
    }

    /**
     * 保存签约数据，调用前要保证该数据不存在
     * @param userBaseId
     * @param idNumber
     * @param callbackData
     */
    private void saveMerchantSignHjtx(String userBaseId, String idNumber, String callbackData) {
        MerchantSignHjtx signHjtx = new MerchantSignHjtx();
        signHjtx.setUserBaseId(userBaseId);
        signHjtx.setIdNumber(idNumber);
        signHjtx.setCallbackData(callbackData);
        int inSing = merchantSignHjtxMapper.insertSelective(signHjtx);
        BizAssertUtils.assertOne(inSing, "添加签约V2记录失败");
    }

    /**
     * 查询平台商户余额
     */
    @Override
    public BigDecimal getBalance() {
        log.info("汇积天下查询平台商户余额");

        Map<String, String> typeParams = payTypeApiService.getPayParams(PayTypeEnum.HJTXV2, PayFromEnum.yxy_withdraw_ally_all);
        HjtxAgentpayParamV2DTO paramDTO = JsonUtils.convertValue(typeParams, HjtxAgentpayParamV2DTO.class);

        Map<String, String> contentMap = new HashMap<>();
        contentMap.put("main_id", "9");

        HjtxBaseDTO baseDTO = new HjtxBaseDTO();
        baseDTO.setChannel(paramDTO.getChannel());
        baseDTO.setMethod("getBusBalance");

        String resp = "";
        try {
            PrivateKey kuaishouPrivateKey = HjtxRSAUtils.getPrivateKey(paramDTO.getKuaishouprivatekey());
            KLog.info("【汇积天下查询平台商户余额】加密前参数:{}", contentMap);
            baseDTO.setBiz_content(HjtxRSAUtils.encryptByPrivateKey(HjtxRSAUtils.getEncodeSignContentByMap(contentMap,
                    true), kuaishouPrivateKey));
            baseDTO.setSign(HjtxRSAUtils.rsa256Sign(HjtxRSAUtils.getEncodeSignContentByObj(baseDTO), kuaishouPrivateKey));

            String url = paramDTO.getAgentpayurl();
            KLog.info("【汇积天下查询平台商户余额】准备请求接口,url:{}, 入参:{}", url, baseDTO.toJSON());
            resp = HttpClientUtil.httpPostRequest(url, baseDTO.toJSON());
            KLog.info("【汇积天下查询平台商户余额】接口响应:{}", resp);

        } catch (Exception e) {
            KLog.warn("【汇积天下查询平台商户余额】请求失败:{},入参:{}", e.getMessage());
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.isBlank(resp)) {
            KLog.warn("【汇积天下查询平台商户余额】接口返回空");
            throw new WichdrawWaitException("接口返回空");
        }

        JSONObject respObj = JSON.parseObject(resp);
        if (!respObj.getBoolean("res")) {
            KLog.warn("【汇积天下查询平台商户余额】接口响应未成功");
            throw new WichdrawWaitException(respObj.getString("msg"));
        }
        JSONObject respData;

        try {
            String resDataEncrypt = respObj.getString("data");
            PublicKey hjtxPublicKey = HjtxRSAUtils.getPublic(paramDTO.getHjtxpublickey());
            // 公钥解密
            String resDataStr = HjtxRSAUtils.decryptByPublicKey(resDataEncrypt, hjtxPublicKey);
            respData = JSON.parseObject(resDataStr);
            KLog.info("【汇积天下查询平台商户余额】响应结果解密后：{}", UnicodeUtil.toString(resDataStr));

        } catch (Exception e) {
            KLog.error("【汇积天下查询平台商户余额】响应结果解密异常:{}", e.getMessage());
            throw new WichdrawWaitException(e.getMessage());
        }

        if (StringUtils.equals(respData.getString("res_code"), "000000")) {
            // 响应成功
            JSONArray balanceList = respData.getJSONArray("balance_list");
            JSONObject balanceObj = (JSONObject) balanceList.get(0);
            return balanceObj.getBigDecimal("balance");
        }  else {
            throw new WichdrawWaitException("【汇积天下查询平台商户余额】响应结果状态异常");
        }
    }


    /**
     * 因汇积天下按月创建任务,一般为半年创建一次， 每次提现必须传入当月任务代码
     * 该方法为查询当月任务代码
     * @param taskCodeJsonStr 格式 {"202311":"aaaaaaaaaaaaa","202312":"bbbbbbbbbbb"}
     * @return String
     */
    private String getHjtxTaskCode(String taskCodeJsonStr) {
        if (StringUtils.isNotBlank(taskCodeJsonStr)) {
            JSONObject jsonObject = JSONObject.parseObject(taskCodeJsonStr);

            // 当前月
            String currentMonth = LocalDateUtils.localDateByFormat(LocalDate.now(), "yyyyMM");
            return jsonObject.getString(currentMonth);
        }
        return null;
    }
}
